package main;

import com.item.manager.Template;
import encounter.FallenHeroEncounter;
import item.Item;
import location.Location;
import weapon.Weapon;

import java.util.*;

public class Hero extends BasicCreature {
    private Location location;

    private int traps;
    private int openLocks;
    private int evade;
    private int nature;
    private int sneak;

    private int copper = 0;
    private boolean deceased = false;

    private final Map<Item, Integer> items = new HashMap<>();
    private final Deque<String> heroEvents = new ArrayDeque<>();
    private final List<Trophy> trophies = new ArrayList<Trophy>();

    private Optional<Weapon> weapon = Optional.empty();
    private Optional<Hero> rescue = Optional.empty();

    public Hero(Location location, Template template) {
        super(new CreatureTemplate(template));

        this.location = location;

        this.traps = template.findAttr("Traps").asInt();
        this.openLocks = template.findAttr("OpenLocks").asInt();
        this.evade = template.findAttr("Evade").asInt();
        this.nature = template.findAttr("Nature").asInt();
        this.sneak = template.findAttr("Sneak").asInt();

    }

    public void process() {
        if (!isAlive()) {
            if (!isDeceased()) {
                deceased = RandomProvider.chance(0.01 / maxHealth);
                if (isDeceased()) {
                    Logger.log(this + " deceased.");
                    this.addEvent("Deceased...");
                }
            }

            return;
        }

        location.visit(this);
    }

    public void moveTo(Location location) {
        this.location = location;
        this.rescue.ifPresent((rescue) -> rescue.moveTo(location));
    }

    public int getCopper() {
        return this.copper;
    }

    public void updateCopper(int copper) {
        this.copper += copper;
    }

    public void addItem(Item item, int count) {
        Logger.log(this + " gets " + count + " " + item);
        items.merge(item, count, (oldCount, newCount) -> oldCount + newCount);
    }

    public Map<Item, Integer> getItems() {
        return this.items;
    }

    public int getTraps() {
        return energyAdjust(this.traps);
    }

    public int getOpenLocks() {
        return energyAdjust(this.openLocks);
    }

    public int getEvade() {
        return energyAdjust(this.evade);
    }

    public void removeItem(Item item, int count) {
        items.compute(item, (itm, oldCount) -> count >= oldCount ? null : oldCount - count);
    }

    public void addEvent(String event) {
        this.heroEvents.addFirst(event);
        while (this.heroEvents.size() > 10) {
            this.heroEvents.removeLast();
        }
    }

    public int getNature() {
        return energyAdjust(this.nature);
    }

    public int getSneak() {
        return energyAdjust(this.sneak);
    }

    public List<Trophy> getTrohpies() {
        return this.trophies;
    }

    public void addTrophy(Trophy trophy) {
        trophies.add(trophy);
    }

    public boolean isDeceased() {
        return this.deceased;
    }

    public boolean rescue(Hero fallenHero) {
        if (!this.rescue.isPresent()) {
            this.rescue = Optional.of(fallenHero);
            return true;
        }

        return false;
    }

    public Optional<Hero> getRescue() {
        return this.rescue;
    }

    public void clearRescue() {
        this.rescue = Optional.empty();
    }

    @Override
    public void updateHealth(double health) {
        boolean wasAlive = isAlive();

        super.updateHealth(health);

        if (wasAlive && !isAlive()) {
            Logger.log(this + " is fallen.");
            this.location.addEncounter(new FallenHeroEncounter(this), 1);
        }
    }

    public void equipWeapon(Weapon weapon) {
        Logger.log(this + " equips " + weapon);
        this.addEvent("Equips " + weapon);
        this.weapon = Optional.of(weapon);
    }

    @Override
    public int getCombat() {
        int weaponCombat = this.weapon.map(w -> w.getCombat()).orElse(0);
        return energyAdjust(this.combat + weaponCombat);
    }
}
